package com.mars.module.workflow.service.impl;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.mars.common.response.PageInfo;
import com.mars.framework.exception.ServiceException;
import com.mars.module.workflow.request.ListModelRequest;
import com.mars.module.workflow.request.ModelRequest;
import com.mars.module.workflow.service.IProcessModelService;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.activiti.bpmn.converter.BpmnXMLConverter;
import org.activiti.bpmn.model.BpmnModel;
import org.activiti.bpmn.model.GraphicInfo;
import org.activiti.editor.constants.ModelDataJsonConstants;
import org.activiti.editor.language.json.converter.BpmnJsonConverter;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.Model;
import org.activiti.engine.repository.ModelQuery;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.servlet.http.HttpServletResponse;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Map;
import java.util.Objects;

import static org.activiti.editor.constants.ModelDataJsonConstants.MODEL_DESCRIPTION;
import static org.activiti.editor.constants.ModelDataJsonConstants.MODEL_NAME;

/**
 * 功能描述
 *
 * @author 程序员Mars
 * @version 1.0
 * @date 2024-01-22 17:19:03
 */
@Slf4j
@Service
@AllArgsConstructor
@SuppressWarnings("all")
public class ProcessModelServiceImpl implements IProcessModelService {

    private final RepositoryService repositoryService;

    @Override
    public String create(ModelRequest request) {
        ObjectMapper objectMapper = new ObjectMapper();
        ObjectNode editorNode = objectMapper.createObjectNode();
        editorNode.put("id", "canvas");
        editorNode.put("resourceId", "canvas");
        ObjectNode stencilSetNode = objectMapper.createObjectNode();
        stencilSetNode.put("namespace", "http://b3mn.org/stencilset/bpmn2.0#");
        editorNode.put("stencilset", stencilSetNode);

        ObjectNode modelObjectNode = objectMapper.createObjectNode();
        modelObjectNode.put(MODEL_NAME, request.getName());
        modelObjectNode.put(ModelDataJsonConstants.MODEL_REVISION, 1);
        modelObjectNode.put(MODEL_DESCRIPTION, StringUtils.defaultString(request.getDescription()));

        Model newModel = repositoryService.newModel();
        newModel.setMetaInfo(modelObjectNode.toString());
        newModel.setName(request.getName());
        newModel.setKey(StringUtils.defaultString(request.getKey()));
        repositoryService.saveModel(newModel);
        repositoryService.addModelEditorSource(newModel.getId(), editorNode.toString().getBytes(StandardCharsets.UTF_8));
        return newModel.getId();
    }

    @Override
    public void deploy(ModelRequest request) throws IOException {
        Model modelData = repositoryService.getModel(request.getModelId());
        if (Objects.isNull(modelData)) {
            throw new ServiceException("模型不存在");
        }
        ObjectNode modelNode = (ObjectNode) new ObjectMapper().readTree(repositoryService.getModelEditorSource(modelData.getId()));
        if (Objects.isNull(modelNode)) {
            throw new ServiceException("BPMN XML不存在");
        }
        byte[] bpmnBytes = null;
        BpmnModel model = new BpmnJsonConverter().convertToBpmnModel(modelNode);
        Map<String, GraphicInfo> modelLocationMap = model.getLocationMap();
        if (MapUtils.isEmpty(modelLocationMap)) {
            throw new ServiceException("未定义流程图");
        }
        bpmnBytes = new BpmnXMLConverter().convertToXML(model);
        String processName = modelData.getName() + ".bpmn20.xml";
        Deployment deployment = repositoryService.createDeployment().name(modelData.getName()).addString(processName, new String(bpmnBytes, "UTF-8")).deploy();
        log.info("部署成功，部署ID=" + deployment.getId());
    }

    @Override
    public void export(ModelRequest request, HttpServletResponse response) throws IOException {
        Model modelData = repositoryService.getModel(request.getModelId());
        if (Objects.isNull(modelData)) {
            throw new ServiceException("当前模型不存在");
        }
        BpmnJsonConverter jsonConverter = new BpmnJsonConverter();
        JsonNode editorNode = new ObjectMapper().readTree(repositoryService.getModelEditorSource(modelData.getId()));
        BpmnModel bpmnModel = jsonConverter.convertToBpmnModel(editorNode);

        // 流程非空判断
        if (!CollectionUtils.isEmpty(bpmnModel.getProcesses())) {
            BpmnXMLConverter xmlConverter = new BpmnXMLConverter();
            byte[] bpmnBytes = xmlConverter.convertToXML(bpmnModel);
            ByteArrayInputStream in = new ByteArrayInputStream(bpmnBytes);
            String filename = bpmnModel.getMainProcess().getId() + ".bpmn";
            response.setHeader("Content-Disposition", "attachment; filename=" + filename);
            IOUtils.copy(in, response.getOutputStream());
            response.flushBuffer();
        }
    }

    @Override
    public void remove(String ids) {
        repositoryService.deleteModel(ids);
    }

    @Override
    public PageInfo<Model> getModelList(ListModelRequest request) {
        ModelQuery modelQuery = repositoryService.createModelQuery();
        modelQuery.orderByLastUpdateTime().desc();
        if (StringUtils.isNotBlank(request.getKey())) {
            modelQuery.modelKey(request.getKey());
        }
        if (StringUtils.isNotBlank(request.getName())) {
            modelQuery.modelNameLike("%" + request.getName() + "%");
        }
        List<Model> resultList = modelQuery.listPage((request.getPageNo() - 1) * request.getPageSize(), request.getPageSize());
        return new PageInfo<>((int) modelQuery.count(), resultList);
    }
}
